package main

import (
	"bytes"
	"flag"
	"fmt"
	"git.apache.org/thrift.git/lib/go/thrift"
	"lpaiche.com/hammer/lib/gen-go/Services/AuctionService"
	u "lpaiche.com/utils"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
)

type Status struct {
	EndNums     int64
	BiddingNums int64
	StartTime   int64
	LifeTime    int64
	RoutineNums int
}

var (
	conf      u.Config
	status    Status
	statusSck string
)

func main() {

	var wg sync.WaitGroup
	startTime := flag.String("starttime", "", "-starttime 指定开始日期时间 如：2016-06-06 15:15:00")
	orderId := flag.Int64("orderid", 0, "-orderid 指定结束拍单号 如：8826")
	isViewStatus := flag.Bool("status", false, "-status=true 指定结束拍单号 如：8826")
	isClean := flag.Bool("clean", false, "-clean=true 清除信息,慎用会清空历史未结束的拍单信息")
	flag.Parse()

	conf = getConf()
	u.Trace(conf)

	if *isClean {
		tmpFile := conf.Hammer.TmpFile
		os.Remove(tmpFile)
		return
	}
	statusSck = conf.Hammer.UnixSocket
	if *isViewStatus {
		us := u.NewUnixSocket(statusSck)
		r := us.ClientSendContext("status")
		fmt.Println(r)
		return
	} else {
		if *orderId == 0 && *startTime == "" {
			us := u.NewUnixSocket(statusSck)
			us.SetContextHandler(hanlder)
			go us.StartServer()
		}
	}

	go printNumGoroutine()
	if *orderId > 0 {
		//手动发起结束拍单
		u.Trace("手动指定结束拍单：" + strconv.FormatInt(*orderId, 10))
		endTrigger(*orderId, true)
	}
	if *startTime != "" {
		//手动发起thrift
		u.Trace("手动指定开始时间：" + *startTime)
		t, _ := time.Parse("2006-01-02 15:04:05", *startTime)
		goStartTrigger(t)
	}

	if *orderId == 0 && *startTime == "" {
		status.StartTime = time.Now().Unix()
		//恢复数时拍单
		recoverOrder()

		//按时间点发起thrift
		interval := int64(conf.Hammer.Interval)
		for {
			now := time.Now()
			go goStartTrigger(now)
			wt := (interval - (now.Unix() % interval))
			time.Sleep(time.Second * time.Duration(wt))
		}
		wg.Wait()
	}
}

//unixsocket请求处理
func hanlder(context string) string {
	if context == "status" {
		tm := time.Unix(status.StartTime, 0)
		r := "已结束：" + strconv.FormatInt(status.EndNums, 10) +
			"\n竞拍中:" + strconv.FormatInt(status.BiddingNums, 10) +
			"\n启动时间:" + tm.Format("2006-01-02 03:04:05 PM") +
			"\n运行秒数:" + strconv.FormatInt(status.LifeTime, 10) +
			"\n当前协程数:" + strconv.FormatInt(int64(status.RoutineNums), 10) + "\n"
		return r
	} else {
		return "正在运行中..."
	}
}

//监控协程数
func printNumGoroutine() {
	for {
		status.RoutineNums = runtime.NumGoroutine()
		status.LifeTime++
		//u.Trace("runtime协程总数:" + strconv.Itoa(runtime.NumGoroutine()) + " time:" + time.Now().Format("2006-01-02 03:04:05 PM") + " 竞拍中:" + strconv.FormatInt(status.BiddingNums, 10) + " 已结束:" + strconv.FormatInt(status.EndNums, 10))
		time.Sleep(time.Second * time.Duration(1))
	}
}

// init config
func getConf() u.Config {
	u.InitConfig("./conf/hammer_config.toml")
	conf := u.GetConfig()
	return conf
}

//thrift client
func getThriftClient(thriftHostURL string) (client *AuctionService.AuctionServiceClient) {
	http, err := thrift.NewTHttpPostClient(thriftHostURL)
	if err != nil {
		u.Trace("Error thrift 18 opening http failed: %v \n")
		u.Trace(err)
		return
	} else {
		// 数据传输方式
		transport := thrift.NewTBufferedTransport(http, 24*1024*1024)
		// 数据格式
		var protocol thrift.TProtocol = thrift.NewTBinaryProtocolTransport(transport)
		protocol = thrift.NewTMultiplexedProtocol(protocol, "AuctionService")
		client = AuctionService.NewAuctionServiceClientProtocol(transport, protocol, protocol)
		err := transport.Open()
		if err != nil {
			u.Trace("Error thrift 25 opening transport failed: %v \n")
			u.Trace(err)
		}
	}
	return client
}

//恢复数时拍单
func recoverOrder() {
	path := conf.Hammer.TmpFile
	result_str := ""
	if _bool, _ := u.FilePathExists(path); !_bool {
		return
	}
	result_str = u.ReadStrFile(path)
	result := unResult(result_str)
	orderList := result.Success
	status.BiddingNums = int64(len(orderList))
	fmt.Println(orderList)
	if len(orderList) > 0 {
		for _, order := range orderList {
			go func() {
				u.Trace("恢复竞拍：" + strconv.FormatInt(order.OrderId, 10))
				elapsedTime := max(order.ActElapsedTime, order.EstElapsedTime)
				sleepLen := (order.BiddingStartTime + elapsedTime) - time.Now().Unix()

				//数时后发起结束
				time.Sleep(time.Second * time.Duration(sleepLen))

				ok := endTrigger(order.OrderId, false)
				fmt.Println(ok)
				if ok == true {
					//清除暂存中的数时拍单
					rmTmpSaveOrder(order.OrderId)
				}
			}()
		}
	}
}

//请求thrift开始
func goStartTrigger(now time.Time) {
	inow := now.Unix()
	u.Trace("发起开始:---------------------------------------------------------------" + now.Format("2006-01-02 03:04:05 PM"))
	u.Trace("发起开始时间点：" + strconv.FormatInt(inow, 10) + " " + now.Format("2006-01-02 03:04:05 PM"))
	client := getThriftClient(conf.Thrift.Biddinghost)
	defer client.Transport.Close()

	//调用应用示例
	orderList, err := client.StartTrigger(inow)
	if err != nil {
		u.Trace(err)
		return
	}
	u.Trace("开始结果：" + strconv.FormatInt(inow, 10))
	u.Trace(orderList)
	if len(orderList) > 0 {
		enWaitEnd(orderList)
	}

}

//请求thrift结束
func endTrigger(orderId int64, isForce bool) bool {
	u.Trace("发起结束：---------------------------------------------------------------" + strconv.FormatInt(orderId, 10))
	client := getThriftClient(conf.Thrift.Biddinghost)
	defer client.Transport.Close()

	//调用应用示例
	orderList, err := client.EndTrigger(orderId, isForce)
	if err != nil {
		u.Trace(err)
		return false
	}
	u.Trace("结束结果：" + strconv.FormatInt(orderId, 10))
	u.Trace(orderList)

	if len(orderList) < 1 {
		return false
	}

	for _, order := range orderList {
		elapsedTime := max(order.ActElapsedTime, order.EstElapsedTime)
		sleepLen := (order.BiddingStartTime + elapsedTime) - time.Now().Unix()
		if sleepLen > 0 {
			//继续等待结束
			enWaitEnd(orderList)
		}
	}

	return true

}

//开启等待结束
func enWaitEnd(orderList map[int64]*AuctionService.OrderSceneMsg) {
	for _, v := range orderList {
		go goOrderToTimeEnd(v)
	}
}

//拍单到指定时间点结束
func goOrderToTimeEnd(order *AuctionService.OrderSceneMsg) {
	elapsedTime := max(order.ActElapsedTime, order.EstElapsedTime)
	sleepLen := (order.BiddingStartTime + elapsedTime) - time.Now().Unix()
	if sleepLen < 1 {
		return
	}
	//暂存数时拍单
	addTmpSaveOrder(order)
	//数时后发起结束
	time.Sleep(time.Second * time.Duration(sleepLen))
	ok := endTrigger(order.OrderId, false)
	if ok == true {
		//清除暂存中的数时拍单
		rmTmpSaveOrder(order.OrderId)
	}
}

//暂存数时拍单
func addTmpSaveOrder(o *AuctionService.OrderSceneMsg) {
	status.BiddingNums++
	path := conf.Hammer.TmpFile
	result := AuctionService.NewAuctionServiceStartTriggerResult()
	orderList := make(map[int64]*AuctionService.OrderSceneMsg)

	result_str := ""
	if _bool, _ := u.FilePathExists(path); _bool {
		result_str = u.ReadStrFile(path)
	}
	if (bytes.Count([]byte(result_str), nil) - 1) > 1 {
		//反串
		result = unResult(result_str)
		orderList = result.Success
	}
	orderList[o.OrderId] = o
	result.Success = orderList

	//借助thrift序列化并存储
	result_str = enResult(result)
	_, err1 := u.RWriteBufFile(path, result_str)
	if err1 != nil {
		u.Trace(err1)
	}
	u.Trace("数时拍单开始：" + strconv.FormatInt(o.OrderId, 10))
	u.Trace(o)
}

//从暂存中移除数时拍单
func rmTmpSaveOrder(orderId int64) {
	status.BiddingNums--
	path := conf.Hammer.TmpFile
	result_str := ""
	if _bool, _ := u.FilePathExists(path); !_bool {
		return
	}
	result_str = u.ReadStrFile(path)
	result := unResult(result_str)

	success := result.Success
	delete(success, orderId)
	result.Success = success

	result_str = enResult(result)
	_, err1 := u.RWriteBufFile(path, result_str)
	if err1 != nil {
		u.Trace(err1)
	}
	u.Trace("数时拍单结束：" + strconv.FormatInt(orderId, 10))
	status.EndNums++
}

//反串
func unResult(result_str string) (result *AuctionService.AuctionServiceStartTriggerResult) {
	//借助thrift反序列化
	result = AuctionService.NewAuctionServiceStartTriggerResult()
	t1 := thrift.NewTDeserializer()
	pf2 := thrift.NewTJSONProtocolFactory()
	t1.Protocol = pf2.GetProtocol(t1.Transport)
	if err := t1.ReadString(result, result_str); err != nil {
		u.Trace("Unable to Deserialize struct:")
		u.Trace(err)
	}
	return result
}

//串化
func enResult(result *AuctionService.AuctionServiceStartTriggerResult) (result_str string) {
	//借助thrift序列化
	t := thrift.NewTSerializer()
	pf := thrift.NewTJSONProtocolFactory()
	t.Protocol = pf.GetProtocol(t.Transport)
	result_str, err := t.WriteString(result)
	if err != nil {
		u.Trace("Unable to Serialize struct:")
		u.Trace(err)
	}
	return result_str
}

//最大数
func max(a int64, b int64) int64 {
	if a > b {
		return a
	}
	return b
}
